package game.ghostleg;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;

import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.*;
import android.widget.Button;
import android.widget.TextView;

public class GhostLegView extends View implements Runnable {
	
	protected static int MAX_COL = 4;
	private static Paint playerPaint;
	
	static {
		playerPaint = new Paint();
		playerPaint.setColor(Color.WHITE);
		playerPaint.setStyle(Style.FILL);
		playerPaint.setTextSize(16);
	}
	
	public String[]	IPAddress;
	public String	hostIPAddress;
	
	public int screen_x, screen_y;
	public int selected_col, selected_row;
	int factor=2;
	int tWidth=720/factor, tHeight=1280/factor;
	int RowDivision=6;
	int ColDivision=160;
	
	public int hostID;
	public int connectedPlayer;
	public PlayerInfo playerInfo;
	// number of players
	public int playerNum;
	// the start position of every player
	// position is zero based ( i.e. 0-3 )
	public int[] startPos;
	// the connection from each sources to their corresponding result
	// CAUTION : updated only at the last moment!
	public int[] result;
	// the items in each result
	public Item [] items=null;
	public Interval [] intervals=null;
	public boolean	isStartPosVisible,
					isItemsVisible,
					isGameEnded;
	// end time, count in milli-seconds
	public int time;
	public List<SocketData> inData, outData;
	Semaphore inLock, outLock;
	public boolean isHost;
	
	private Paint	mColLinePaint,
					mRowConfirmedLinePaint,
					mRowSelectedLinePaint,
					mResultLinePaint,
					mTextPaint;
	
	protected Button	mConfirmButton,
						mNextButton;
	
    private RefreshHandler mHandler;
        
    private Thread[]	inSocket;
    private Thread		outSocket;
    private OutSocket	hostOutSocket;
    
    public int inDirty, outDirty;
    {
    	inDirty=outDirty=0;
    }
    
	public GhostLegView(Context context, AttributeSet attrs) {
		super(context,attrs);
	}
	
	public void startSetup(boolean isHost, PlayerInfo pPlayerInfo, int pPlayerNum) {
		int i;
		this.time = -1; 
		this.selected_row = this.selected_col = 1;
		this.outData = Collections.synchronizedList(new LinkedList<SocketData>());
		this.inData = Collections.synchronizedList(new LinkedList<SocketData>());
		this.IPAddress = new String[4];
		this.IPAddress[0] = "";
		this.IPAddress[1] = "175.159.114.10";
		this.IPAddress[2] = "";
		this.IPAddress[3] = "175.159.108.127";
		
		this.intervals	= new Interval[MAX_COL-1];
		for(i=0;i<MAX_COL-1;i++)
			this.intervals[i] = new Interval();
		System.err.println("DONE");
		this.result		= null;
		this.inLock		= new Semaphore(1000);
		this.outLock	= new Semaphore(1000);
		this.mColLinePaint = new Paint();
		this.mColLinePaint.setColor(0xFFFFFFFF);
		this.mColLinePaint.setStrokeWidth(2);
		this.mColLinePaint.setStyle(Style.STROKE);
		this.mRowSelectedLinePaint = new Paint();
		this.mRowSelectedLinePaint.setColor(Color.GRAY);
		this.mRowSelectedLinePaint.setStrokeWidth(1);
		this.mRowSelectedLinePaint.setStyle(Style.STROKE);
		this.mRowConfirmedLinePaint = new Paint();
		this.mRowConfirmedLinePaint.setColor(Color.BLUE);
		this.mRowConfirmedLinePaint.setStrokeWidth(2);
		this.mRowConfirmedLinePaint.setStyle(Style.STROKE);
		this.mResultLinePaint = new Paint();
		this.mResultLinePaint.setColor(Color.RED);
		this.mResultLinePaint.setStrokeWidth(3);
		this.mResultLinePaint.setStyle(Style.STROKE);	
		this.mTextPaint = new Paint();
		this.mTextPaint.setColor(Color.WHITE);
		this.mTextPaint.setStrokeWidth(2);
		this.mTextPaint.setStyle(Style.STROKE);
		this.mTextPaint.setTextSize(14);
		
		this.playerInfo 	= pPlayerInfo;
		this.playerNum		= pPlayerNum;
		this.isHost			= isHost;
		this.isGameEnded	= false;
		this.connectedPlayer = 1;
		
		this.items			= new Item[4];
		
		if ( isHost )
		{
			int j;
			// randomization
			this.time		= 30*1000; // 30s
			this.startPos	= new int[MAX_COL];
			this.items		= new Item[MAX_COL];

			for(i=0;i<MAX_COL;i++)
			{
				this.startPos[i]=i;
				j=(int)(Math.random()*MAX_COL);
				// perform swapping
				/*
				this.startPos[i]=this.startPos[i]^this.startPos[j];
				this.startPos[j]=this.startPos[i]^this.startPos[j];
				this.startPos[i]=this.startPos[i]^this.startPos[j];
				*/
				this.items[i]=Item.RandItem();
			}
			this.isStartPosVisible = this.isItemsVisible = true;
			
			try {
				outLock.acquire();
				outData.add(new SocketData(1,playerNum));
				outData.add(new SocketData(10,time));
				outData.add(new SocketData(2,startPos));
				for(i=0;i<MAX_COL;i++)
					outData.add(new SocketData(5,items[i],i));
				outData.add(new SocketData(6,isStartPosVisible));
				outData.add(new SocketData(7,isItemsVisible));
				outLock.release();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		}
		else
		{
			this.isItemsVisible		= false;
			this.isStartPosVisible	= false;
		}
	}
	public void setScreenSize(int width, int height) {
		this.screen_x = width;
		this.screen_y = height;
	}
	public void start() {
//		this.inSocket.start();
//		this.outSocket.start();
		this.mHandler = new RefreshHandler();
	}
	
	public int getRowY(int i) {
		return (80+RowDivision*i)/factor;
	}
	public int getColX(int i) {
		return (100+ColDivision*i)/factor;
	}
	
	public void setConfirmButton ( Button button ) {
		this.mConfirmButton = button;
//		this.mConfirmButton.setOnClickListener(new TempButtonListener());
		this.mConfirmButton.setOnClickListener(new ConfirmButtonListener());
	}
	
	public void setNextButton ( Button button ) {
		this.mNextButton = button;
		this.mNextButton.setVisibility(View.INVISIBLE);
	}
	
	public void setServerSocket() {
		System.err.printf("Inside setServerSocket, isHost=%d\n",isHost?1:0);
		int port = 10000;
		int i;
		InSocket tInSocket;
		this.inSocket = new Thread[4];
		if ( this.isHost )
		{
			for(i=0;i<4;i++)//this.playerNum;i++)
				if(i!=this.playerInfo.getPlayerID())
				{
					System.err.printf("%d %d\n",i,this.playerInfo.getPlayerID());
					tInSocket = new InSocket(port+(i<<1),this);
					tInSocket.ididid=i;
					this.inSocket[i] = new Thread(tInSocket);
					(this.inSocket[i]).start();
				}
		}
		else
		{
			for(i=0;i<4;i++)//this.playerNum;i++)
				if(i==this.playerInfo.getPlayerID())
				{
					System.err.printf("%d %d\n",i,this.playerInfo.getPlayerID());
					tInSocket = new InSocket(port+1+(i<<1),this);
					tInSocket.ididid=i;
					this.inSocket[i] = new Thread(tInSocket);
					(this.inSocket[i]).start();
				}
		}
	}
	public void setOutSocket() {
		int port = 10000;
		if ( this.isHost )
		{
			hostOutSocket = new OutSocket(3,this);
		}
		else
		{
			OutSocket tOutSocket;System.err.println("1");
			tOutSocket = new OutSocket(1,this);System.err.println("1");
			System.err.println(this.IPAddress[this.hostID]+" "+(port+((this.playerInfo.getPlayerID())<<1)));
			tOutSocket.addConnection(this.IPAddress[this.hostID], port+((this.playerInfo.getPlayerID())<<1));System.err.println("1");
			try {
				this.outLock.acquire();System.err.println("1");
				this.outData.add(new SocketData(11,this.playerInfo.getPlayerID()));System.err.println("1");
				this.outLock.release();System.err.println("1");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			this.outSocket = new Thread(tOutSocket);System.err.println("1");
			this.outSocket.start();System.err.println("1");
		}
	}
	public boolean insert ( int p_rowNum, int p_colNum, int p_playerNum ) {
		System.out.printf("In GhostLegWorld.Insert(%d,%d,%d), ",p_rowNum,p_colNum,p_playerNum);
		if ( time <= 0 || ( this.isHost && this.connectedPlayer < this.playerNum) )
		{
			System.out.printf("return false : game is not initiated(time <= 0||not connected)\n");
			return false;
		}
		if ( 0 <= p_colNum && p_colNum < MAX_COL && 1 <= p_playerNum && p_playerNum <= 4 )
		{
			System.out.println("Validation passed");
			
			if(MAX_COL==2)
			{
				if(intervals[0].isConnected(p_rowNum)||intervals[1].isConnected(p_rowNum))
					return false;
			}
			else
			{
				if(p_colNum>0&&(intervals[p_colNum-1].isConnected(p_rowNum)))
					return false;
				if(p_colNum<MAX_COL-1&&(intervals[p_colNum].isConnected(p_rowNum)))
					return false;
			}
			return intervals[p_colNum].insert(p_rowNum, p_playerNum);
		}
		System.out.println("Failed : Out of range");
		return false;
	}
	public void computeResult() {
		int i,j,k;
		result = new int[MAX_COL];
		// seems to be useless
		for(i=0;i<MAX_COL;i++)
			this.result[i] = i;
		
		int tmp_result[] = new int[MAX_COL];
		for(i=0;i<Interval.MAX_ROW;i++)
		{
			for(j=0;j<MAX_COL;j++)
			{
				tmp_result[j]=j;
				if(j>0&&intervals[j-1].isConnected(i))
				{
					k=tmp_result[j-1];
					tmp_result[j-1]=tmp_result[j];
					tmp_result[j]=k;
				}
			}
			for(j=0;j<MAX_COL;j++)
				result[j]=tmp_result[result[j]];
		}
	}
	// players and items are not yet drawn
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		System.err.printf("In onDraw()\n");
		canvas.drawText(String.format("Time : %d",Math.max(0,this.time/1000)), getColX(MAX_COL), getRowY(10), mTextPaint);
		canvas.drawText(String.format("IN:%d %d",inData.size(),inDirty), getColX(MAX_COL), getRowY(0), mTextPaint);
		canvas.drawText(String.format("OUT:%d %d",outData.size(),outDirty),getColX(MAX_COL),getRowY(5),mTextPaint);
		if ( time == -1 )
			return ;
		
		int i,j;
		
		for(i=0;i<MAX_COL;i++)
			canvas.drawLine(getColX(i), getRowY(0), getColX(i), getRowY(Interval.MAX_ROW), mColLinePaint);
		
		if(intervals!=null)
		for(i=0;i<MAX_COL-1;i++)
			for(j=0;j<Interval.MAX_ROW;j++)
				if(intervals[i].isConnected(j))
					canvas.drawLine(getColX(i),getRowY(j),getColX(i+1),getRowY(j),mRowConfirmedLinePaint);
				else if ( i==selected_col && j==selected_row )
					canvas.drawLine(getColX(i),getRowY(j),getColX(i+1),getRowY(j),mRowSelectedLinePaint);
		
		if ( this.isStartPosVisible )
		{
			for(i=0;i<MAX_COL;i++)
				canvas.drawText(String.format("%d",i),getColX(startPos[i]),getRowY(-6),GhostLegView.playerPaint);
		}

//		if(true)
		if ( this.isItemsVisible )
		{
			if(items!=null)
			for(i=0;i<MAX_COL;i++)
				if(items[i]!=null)
			{
				System.err.printf("items[%d] : ",i);
				System.err.println(items[i].toString());
				System.err.println(items[i].getPaint().getTextSize());
				System.err.println(items[i].getPaint().getStyle());
				System.err.println(items[i].getPaint().getColor());
				canvas.drawText(items[i].toString(), getColX(i), getRowY(Interval.MAX_ROW+6), items[i].getPaint());
			}
			else System.err.println("NULL");
		}
		if ( this.isGameEnded )
		{
			synchronized(this.playerInfo) {
				i=this.playerInfo.getPlayerID();
//				canvas.drawText("You have got : "+items[result[i]], getColX(MAX_COL), getRowY(30), this.mTextPaint);
				System.err.printf("In onDraw(isGameEnded):\nPlayerID: %d %d\n",i,this.playerInfo.getPlayerID());
				for(j=0;j<Interval.MAX_ROW;j++)
				{
					if (i>0&&this.intervals[i-1].isConnected(j))
					{
						canvas.drawLine(getColX(i),getRowY(j),getColX(i-1),getRowY(j), mResultLinePaint);
						i-=1;
					} else
					if( i<MAX_COL-1&&this.intervals[i].isConnected(j))
					{
						canvas.drawLine(getColX(i),getRowY(j),getColX(i+1),getRowY(j), mResultLinePaint);
						i+=1;
					}
					canvas.drawLine(getColX(i),getRowY(j),getColX(i),getRowY(j+1), mResultLinePaint);
//					System.err.printf("%d : %d\n",j,i);
				}
			}

		}
		
	}
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		
		int tCurrentX, tCurrentY;
		tCurrentX=(int)event.getX();
		tCurrentY=(int)event.getY();
		
		if(getColX(0)<=tCurrentX&&tCurrentX<=getColX(MAX_COL-1))
			tCurrentX=(tCurrentX-getColX(0))*factor/ColDivision;
		else tCurrentX=-1;
		
		if(getRowY(0)-2<=tCurrentY&&tCurrentY<=getRowY(100)+2)
			tCurrentY=(tCurrentY-(getRowY(0)-2))*factor/RowDivision;
		else tCurrentY=-1;
		
		if(tCurrentX!=-1&&tCurrentY!=-1)
		{
			selected_col=tCurrentX;
			selected_row=tCurrentY;
		}
		
		System.err.printf("%d %d\n", tCurrentX, tCurrentY );
		invalidate();
		
		return true;
	}	
	public boolean update() {
		try {
			this.inLock.acquire();
			SocketData t;
			while(!this.inData.isEmpty())
			{
				t= this.inData.get(0);
				this.inData.remove(0);
				this.process(t);
			}
			this.inLock.release();
		} catch (InterruptedException e) {
			e.printStackTrace();
			return false;
		}
		
		try {
			this.outLock.acquire();
			this.outLock.release();
		} catch ( InterruptedException e ) {
			e.printStackTrace();
			return false;
		}
		
		if(this.time<=0)
		{
			computeResult();
			this.process(new SocketData(3,result));
		}
		
		return this.time<=0;
	}
	public boolean process ( SocketData t ) {
		boolean t_succ=true;
		System.err.printf("In process() : %d\n",t.type);
		switch ( t.type ) {
			case 0:
				this.playerInfo.playerID = t.val_int;
				break;
			case 1:
				this.playerNum = t.val_int;
				break;
			case 2:
				this.startPos = (int[])t.obj;
				break;
			case 3:
				this.result = (int[])t.obj;
				break;
			case 4:
				Bridge b = (Bridge)t.obj;
				t_succ=this.insert(b.row, b.col, b.player);
				break;
			case 5:
				this.items[t.val_int] = new Item((Item)t.obj);
				break;
			case 6:
				this.isStartPosVisible = t.val_bool;
				break;
			case 7:
				this.isItemsVisible = t.val_bool;
				break;
			case 8:
				this.intervals[t.val_int] = (Interval)t.obj;
				break;
			case 9:
				this.intervals = (Interval[])t.obj;
				break;
			case 10:
				this.time = t.val_int;
				break;
			case 11:
				t_succ=this.hostOutSocket.addConnection(this.IPAddress[t.val_int],10000+1+(t.val_int<<1));
				if(t_succ)
					this.connectedPlayer+=1;
				return t_succ;
			default:
				return false;
		}
		if ( this.isHost && t_succ )
		{
			try {
				this.outLock.acquire();
				this.outData.add(t);
				this.outLock.release();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return true;
	}
    private class RefreshHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
        	System.err.println(outData.size());
        	if ( msg.what == 9528 )
        	{
            	GhostLegView.this.mNextButton.setVisibility(View.VISIBLE);
            	GhostLegView.this.invalidate();
            	return;
        	}
        	GhostLegView view = GhostLegView.this;
        	view.isGameEnded = view.update();
    		GhostLegView.this.invalidate();
        }

        public void sleep(long delayMillis) {
        	this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
        }
    };
    private class TempButtonListener implements OnClickListener {
    	public void onClick(View v) {
    		GhostLegView view = GhostLegView.this;
    		System.err.printf("HI\n");
    		if(view.selected_row!=-1&&view.selected_col!=-1)
    		{
    			System.err.printf("HI\n");
    			view.insert(view.selected_row,view.selected_col,view.playerInfo.getPlayerID());
    			view.mHandler.sendEmptyMessage(0);
    			/*
    			try {
					view.inLock.acquire();
	    			view.inData.add(new SocketData(4,new Bridge(view.selected_row,view.selected_col,view.playerInfo.getPlayerID())));
	    			view.inLock.release();
	    			view.selected_row=view.selected_col=-1;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				*/
    		}
    	}
    }   
    private class ConfirmButtonListener implements OnClickListener {

        public void onClick(View v) {
        	GhostLegView view = GhostLegView.this;
        	if(view.selected_row!=-1&&view.selected_col!=-1)
        	{
        		if ( view.isHost )
        		{
        			try {
						view.inLock.acquire();
	        			view.inData.add(new SocketData(4,new Bridge(view.selected_row,view.selected_col,view.playerInfo.playerID)));
	        			view.inLock.release();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
        		}
        		else
        		try {
        			view.outLock.acquire();
        			view.outData.add(new SocketData(4,new Bridge(view.selected_row,view.selected_col,view.playerInfo.playerID)));
        			view.outLock.release();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				view.selected_row=view.selected_col=-1;
        	}
        }
    }
    public void run() {
    	if ( this.isHost )
    	{
    		this.setOutSocket();
    		while ( this.connectedPlayer < this.playerNum )
    		{
    			mHandler.sendEmptyMessage(100);
    			try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    		}
    		this.outSocket = new Thread ( hostOutSocket );
    		this.outSocket.start();
    	}
    	else
    	{
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.setOutSocket();
			System.err.println(10000);
    	}
    	// ** stop client
    	System.err.println("1");
    	while ( time >= -1000 )
    	{
    		if ( 5*1000 <= time && time <= 25*1000 )
    			this.isItemsVisible=false;
    		else this.isItemsVisible=true;
    		this.process(new SocketData(7,isItemsVisible));
			mHandler.sendEmptyMessage(100);
			if(this.isHost)
			{
				this.time-=200;
				this.process(new SocketData(10,this.time));
				if(this.time<=0&&result==null&&this.isHost);
					//this.mConfirmButton.setActivated(false);
			}
    		try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.err.printf("Time : %d\n",time);
//			System.err.printf("PlayerID : %d\n",playerInfo.getPlayerID());
    	}
    	System.err.printf("Result : %d %d %d %d\n",this.result[0],this.result[1],this.result[2],this.result[3]);
    	mHandler.sendEmptyMessage(9528);
    }
    
}